// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Microsoft.Internal
{
    internal static class ReflectionServices
    {
        internal static bool IsOverridable(MethodBase method)
        {
            return (method.IsAbstract || method.IsVirtual) && !method.IsFinal;
        }

        internal static bool TryGetGenericInterfaceType(Type instanceType, Type targetOpenInterfaceType, out Type targetClosedInterfaceType)
        {
            // The interface must be open
            Assumes.IsTrue(targetOpenInterfaceType.IsInterface && targetOpenInterfaceType.IsGenericTypeDefinition);
            Assumes.IsTrue(!instanceType.IsGenericTypeDefinition);

            // if instanceType is an interface, we must first check it directly
            if (instanceType.IsInterface &&
                instanceType.IsGenericType &&
                instanceType.GetGenericTypeDefinition() == targetOpenInterfaceType)
            {
                targetClosedInterfaceType = instanceType;
                return true;
            }

            try
            {
                // Purposefully not using FullName here because it results in a significantly
                //  more expensive implementation of GetInterface, this does mean that we're
                //  takign the chance that there aren't too many types which implement multiple
                //  interfaces by the same name...
                Type targetInterface = instanceType.GetInterface(targetOpenInterfaceType.Name, false);
                if (targetInterface != null &&
                    targetInterface.GetGenericTypeDefinition() == targetOpenInterfaceType)
                {
                    targetClosedInterfaceType = targetInterface;
                    return true;
                }
            }
            catch (AmbiguousMatchException)
            {
                // On the off chance that the type has >1 interfaces that it implements 
                //  with the same name, we can use this to disambiguate... 
                //
                // However, maybe this isn't great because it means that we can end up with 
                //  situations where we implement the same interface over two different types 
                //  and will always hit the slow path through an exception... 

                foreach (Type type in instanceType.GetInterfaces())
                {
                    if (type.IsGenericType &&
                        type.GetGenericTypeDefinition() == targetOpenInterfaceType)
                    {
                        targetClosedInterfaceType = type;
                        return true;
                    }
                }
            }

            targetClosedInterfaceType = null;
            return false;
        }

        internal static bool IsPubliclyVisible(this Type type)
        {
            // For normal types IsVisible == IsPublic
            // For nested types IsVisible answers the question are all my parents and I public
            // or better put is the nested type visible from another assembly.
            return type.IsVisible;
        }

        internal static IEnumerable<Type> GetValidTypes(this Assembly assembly)
        {
            try
            {
                return assembly.GetTypes();
            }
            catch (ReflectionTypeLoadException e)
            {
                return e.Types.Where(type => type != null);
            }
        }

        internal static IEnumerable<PropertyInfo> GetAllProperties(this Type type)
        {
            return type.GetInterfaces().Concat(new Type[] { type }).SelectMany(itf => itf.GetProperties());
        }


    }
}
